package com.bsj.travel.mqtt.auth;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.bsj.travel.cached.redis.RedisCached;
import com.bsj.travel.config.rocketmq.producer.BaseMqPushUtil;
import com.bsj.travel.constant.ActConstants;
import com.bsj.travel.constant.BaseServerTopicConstants;
import com.bsj.travel.constant.NumericalConstants;
import com.bsj.travel.def.common.DO.ProductTopicDO;
import com.bsj.travel.def.common.DTO.mqtt.EventAct;
import com.bsj.travel.def.common.DTO.mqtt.MqttEvent;
import com.bsj.travel.def.common.DTO.mqtt.NetworkEnum;
import com.bsj.travel.def.mqtt.auth.MqttSignature;
import com.bsj.travel.util.ProductTopicUtil;
import com.bsj.travel.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.iot.mqtt.codec.MqttQoS;
import net.dreamlu.iot.mqtt.core.server.auth.IMqttServerPublishPermission;
import org.springframework.beans.factory.annotation.Autowired;
import org.tio.core.ChannelContext;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author yinhao
 * @version 1.0
 * @description mqtt 服务端校验客户端是否有发布权限，可自行按照自己的需求进行业务扩展
 * @date 2023/11/7
 */
@Slf4j
public class MqttPublishPermission implements IMqttServerPublishPermission {

    @Autowired
    private BaseMqPushUtil baseMqPushUtil;

    @Autowired
    private RedisCached redisCached;

    /**
     * 获取基础通讯topic
     */
    private static Set<String> baseTopic;

    /**
     * 是否有发布权限
     *
     * @param context  channelContext
     * @param clientId 客户端ID
     * @param topic    topic
     * @param qoS      MqttQoS
     * @param isRetain 是否保留消息
     * @return true: 有发布权限 false: 无权限
     */
    @Override
    public boolean hasPermission(ChannelContext context, String clientId,
                                 String topic, MqttQoS qoS, boolean isRetain) {
        long millis = System.currentTimeMillis();
        // 校验客户端订阅的topic，校验成功返回true，失败返回false
        MqttSignature mqttAuth = new MqttSignature(clientId);
        var productKey = mqttAuth.getProductKey();
        var deviceName = mqttAuth.getDeviceName();
        // 替换主题中的产品设备信息,还原topicClass
        String topicClass = StringUtils.reductionTopicClass(topic, productKey, deviceName);
        // 获取该产品信息的topicClass,进行判断是否有权限
        if (checkTopicClass(productKey, topicClass)) {
            return true;
        } else {
            log.error("MqttPublishPermission.hasPermission，产品信息获取失败,无法发布。clientId:{}, productKey:{}", clientId, productKey);
            //发布权限异常,记录日志
            MqttEvent<String> mqttEvent = new MqttEvent<>();
            mqttEvent.setClient(mqttAuth.getDeviceName() + "&" + mqttAuth.getProductKey());
            mqttEvent.setTopic(topic);
            mqttEvent.setQos(qoS.value());
            mqttEvent.setTimestamp(millis);
            mqttEvent.setData(topic);
            EventAct eventAct = EventAct.builder()
                    .timestamp(millis)
                    .traceId(RandomUtil.randomString(30))
                    .serveType(2)
                    .operate(ActConstants.PUB)
                    .event(mqttEvent)
                    .build();
            eventAct.setCode(NetworkEnum.MQTT_PUB_FAIL.getCode());
            //失败则记录失败原因
            baseMqPushUtil.sendTopicBytes(BaseServerTopicConstants.TRAVEL_MQTT_MSG_PROCESS_TOPIC,
                    BaseServerTopicConstants.TRAVEL_MQTT_MSG_PROCESS_BEHAVIOR_TAG, JSON.toJSONBytes(eventAct));
            return false;
        }
    }

    /**
     * 判断是否有权限
     *
     * @param productKey 产品key
     * @return
     */
    private boolean checkTopicClass(String productKey, String topicClass) {
        // 为空则初始化
        if (baseTopic == null) {
            Set<String> baseTopicList = redisCached.getBaseTopicClassSet(NumericalConstants.ONE);
            baseTopic = Objects.requireNonNullElseGet(baseTopicList, HashSet::new);
        }
        // 先判断获取基础通讯topic,优先判断基础通讯，直接命中内存中缓存，提高性能
        if (baseTopic.contains(topicClass)) {
            return true;
        } else {
            // 根据productKey，从缓存中获取topicClass，过滤权限为发布
            Set<String> topicClassSet = redisCached.getTopicClassSet(productKey, NumericalConstants.ONE);
            return topicClassSet.contains(topicClass);
        }
    }

    public static void main(String[] args) {
//        long time = System.currentTimeMillis();
//        String s = "{\"code\":200,\"msg\":\"请求成功\",\"data\":[{\"topicId\":4,\"productId\":0,\"type\":1,\"event\":\"NTP\",\"topicClass\":\"/ext/ntp/b11i2pch3mz/${deviceName}/response\",\"purview\":2,\"descr\":\"NTP时钟同步响应\",\"createTime\":\"2024-02-21T10:45:06.000+08:00\",\"productKey\":null},{\"topicId\":27,\"productId\":74,\"type\":3,\"event\":\"CUSTOMIZE\",\"topicClass\":\"/b11i2pch3mz/${deviceName}/user/fa/gd\",\"purview\":1,\"descr\":\"测试\",\"createTime\":null,\"productKey\":null},{\"topicId\":26,\"productId\":74,\"type\":3,\"event\":\"CUSTOMIZE\",\"topicClass\":\"/b11i2pch3mz/${deviceName}/user/ssdfdfsg_s\",\"purview\":1,\"descr\":\"\",\"createTime\":null,\"productKey\":null},{\"topicId\":29,\"productId\":74,\"type\":3,\"event\":\"CUSTOMIZE\",\"topicClass\":\"/b11i2pch3mz/${deviceName}/user/getInfo\",\"purview\":3,\"descr\":\"\",\"createTime\":null,\"productKey\":null},{\"topicId\":25,\"productId\":74,\"type\":3,\"event\":\"CUSTOMIZE\",\"topicClass\":\"/sdfsgs\",\"purview\":1,\"descr\":\"\",\"createTime\":null,\"productKey\":null},{\"topicId\":3,\"productId\":0,\"type\":1,\"event\":\"NTP\",\"topicClass\":\"/ext/ntp/b11i2pch3mz/${deviceName}/request\",\"purview\":1,\"descr\":\"NTP时钟同步请求\",\"createTime\":\"2024-02-21T10:44:56.000+08:00\",\"productKey\":null},{\"topicId\":35,\"productId\":74,\"type\":3,\"event\":\"CUSTOMIZE\",\"topicClass\":\"/b11i2pch3mz/${deviceName}/user/user/pub\",\"purview\":3,\"descr\":\"云想印推送\",\"createTime\":null,\"productKey\":null},{\"topicId\":11,\"productId\":0,\"type\":2,\"event\":\"SETEVENT\",\"topicClass\":\"/sys/b11i2pch3mz/${deviceName}/thing/event/${tsl.event.identifier}/post_reply\",\"purview\":2,\"descr\":\"云端响应事件上报\",\"createTime\":\"2024-02-21T10:54:52.000+08:00\",\"productKey\":null},{\"topicId\":7,\"productId\":0,\"type\":2,\"event\":\"ATTRIBUTE\",\"topicClass\":\"/sys/b11i2pch3mz/${deviceName}/thing/event/property/post\",\"purview\":1,\"descr\":\"设备属性上报\",\"createTime\":\"2024-02-21T10:51:10.000+08:00\",\"productKey\":null},{\"topicId\":6,\"productId\":0,\"type\":1,\"event\":\"SHADOW\",\"topicClass\":\"/shadow/get/b11i2pch3mz/${deviceName}\",\"purview\":2,\"descr\":\"设备接收影子变更\",\"createTime\":\"2024-02-21T10:47:03.000+08:00\",\"productKey\":null},{\"topicId\":1,\"productId\":0,\"type\":1,\"event\":\"OTA\",\"topicClass\":\"/ota/device/inform/b11i2pch3mz/${deviceName}\",\"purview\":1,\"descr\":\"设备上报固件升级信息\",\"createTime\":\"2024-02-21T10:42:29.000+08:00\",\"productKey\":null},{\"topicId\":10,\"productId\":0,\"type\":2,\"event\":\"SETEVENT\",\"topicClass\":\"/sys/b11i2pch3mz/${deviceName}/thing/event/${tsl.event.identifier}/post\",\"purview\":1,\"descr\":\"设备事件上报\",\"createTime\":\"2024-02-21T10:53:59.000+08:00\",\"productKey\":null},{\"topicId\":2,\"productId\":0,\"type\":1,\"event\":\"OTA\",\"topicClass\":\"/ota/device/upgrade/b11i2pch3mz/${deviceName}\",\"purview\":2,\"descr\":\"固件升级信息下行\",\"createTime\":\"2024-02-21T10:42:38.000+08:00\",\"productKey\":null},{\"topicId\":36,\"productId\":74,\"type\":3,\"event\":\"CUSTOMIZE\",\"topicClass\":\"/b11i2pch3mz/${deviceName}/user/pub\",\"purview\":3,\"descr\":\"云想印推送\",\"createTime\":null,\"productKey\":null},{\"topicId\":9,\"productId\":0,\"type\":2,\"event\":\"SETATTR\",\"topicClass\":\"/sys/b11i2pch3mz/${deviceName}/thing/service/property/set\",\"purview\":2,\"descr\":\"设备属性设置\",\"createTime\":\"2024-02-21T10:53:04.000+08:00\",\"productKey\":null},{\"topicId\":28,\"productId\":74,\"type\":3,\"event\":\"CUSTOMIZE\",\"topicClass\":\"/b11i2pch3mz/${deviceName}/user/fasdf/sfasd\",\"purview\":1,\"descr\":\"测试\\n\",\"createTime\":null,\"productKey\":null},{\"topicId\":24,\"productId\":74,\"type\":3,\"event\":\"CUSTOMIZE\",\"topicClass\":\"/bu/order\",\"purview\":1,\"descr\":\"\",\"createTime\":null,\"productKey\":null},{\"topicId\":8,\"productId\":0,\"type\":2,\"event\":\"ATTRIBUTE\",\"topicClass\":\"/sys/b11i2pch3mz/${deviceName}/thing/event/property/post_reply\",\"purview\":2,\"descr\":\"云端响应属性上报\",\"createTime\":\"2024-02-21T10:51:19.000+08:00\",\"productKey\":null},{\"topicId\":5,\"productId\":0,\"type\":1,\"event\":\"SHADOW\",\"topicClass\":\"/shadow/update/b11i2pch3mz/${deviceName}\",\"purview\":1,\"descr\":\"设备影子发布\",\"createTime\":\"2024-02-21T10:46:54.000+08:00\",\"productKey\":null}],\"total\":0}";
////        String topic = "/b11i2pch3mz/13534912306/user/lbsdata";
//        String topic = "/ext/ntp/b11i2pch3mz/13534912306/request";
//        JSONObject jsonObject = JSON.parseObject(s);
//        System.out.println(System.currentTimeMillis() - time);
//        JSONArray data = jsonObject.getJSONArray("data");
//        List<ProductTopicDO> topicDOS = new ArrayList<>();
//        data.stream().forEach(item -> {
//            ProductTopicDO topicDO = JSON.parseObject(item.toString(), ProductTopicDO.class);
//            topicDOS.add(topicDO);
//        });
//        System.out.println(System.currentTimeMillis() - time);
//
//
//        MqttSignature mqttAuth = new MqttSignature("b11i2pch3mz.13534912306|timestamp=1713150218193,_v=sdk-c-3.2.0,securemode=3,signmethod=hmacsha256,lan=C,_ss=1,gw=0,ext=0|");
//
////        String topicClass;
////        topicClass = topic.replace(mqttAuth.getDeviceName(), "${deviceName}");
////        topicClass = topicClass.replace(mqttAuth.getProductKey(), "${productKey}");
////        System.out.println(topicClass);
//
//        AtomicReference<Boolean> validTopic = ProductTopicUtil.isValidTopic(mqttAuth, topicDOS, topic, 2);
//        Boolean aBoolean = validTopic.get();
//        System.out.println(aBoolean);
//        System.out.println(System.currentTimeMillis() - time);

        String topic = "/ota/device/inform/b11i2pch3mz/13534912307";

        var productKey = "b11i2pch3mz";
        var deviceName = "13534912307";
        String topicClass = StringUtils.reductionTopicClass(topic, productKey, deviceName);
        System.out.println(topicClass);
    }
}
